package com.example.fdbus

import android.annotation.SuppressLint
import android.content.Context
import android.util.Log
import com.example.fdbus.annotation.ClassId
import com.example.fdbus.request.RequestBean
import com.example.fdbus.request.RequestParamter
import com.google.gson.Gson
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import org.java_websocket.client.WebSocketClient
import org.java_websocket.handshake.ServerHandshake
import java.lang.Exception
import java.lang.reflect.Method
import java.lang.reflect.Proxy
import java.net.URI
import java.nio.ByteBuffer
import java.util.concurrent.TimeUnit
import java.util.concurrent.locks.ReentrantLock

@SuppressLint("StaticFieldLeak")
object IPCNameManager {
    private val TAG = IPCNameManager::class.java.name

    private lateinit var sContext: Context
    private lateinit var nameCenter: NameCenter
    private val gson by lazy {
        Gson()
    }
    private lateinit var webSocketClient: MyWebSocketClient

    private lateinit var asyncDeferred: Deferred<Unit>

    private val reentrantLock = ReentrantLock()
    private val condition = reentrantLock.newCondition()

    var requestResult = ""

    private val scope: CoroutineScope by lazy {
        CoroutineScope(Dispatchers.IO)
    }

    fun init(context: Context) {
        sContext = context.applicationContext

        nameCenter = NameCenter()
    }

    fun register(clazz: Class<*>) {
        nameCenter.register(clazz)
    }

    fun getNameCenter(): NameCenter {
        return nameCenter
    }

    fun <T> getInstance(clazz: Class<T>, vararg params: Any): T {
        // 服务发现
        sendServerRequest(clazz, null, params, NameServerManager.TYPE_GET)
        return getProxy(clazz)
    }

    fun <T> sendServerRequest(
        clazz: Class<T>,
        method: Method?,
        params: Array<out Any>?,
        type: Int
    ): String {

        /*
            {
              type: 1
              classname: ""
              methodname: ""
              params: []
            }
         */
        var requestParams: Array<RequestParamter?>? = null

        if (params != null && params.isNotEmpty()) {
            requestParams = Array(params.size) {
                null
            }

            params.forEachIndexed { index, it ->
                val javaClass = it.javaClass
                val value = gson.toJson(it)
                val requestParam = RequestParamter(javaClass.name, value)

                requestParams!![index] = requestParam
            }
        } else {
            requestParams = Array<RequestParamter?>(0) {
                null
            }
        }


        val methodName = if (method == null) "getInstance" else method.name
        val className = clazz.getAnnotation(ClassId::class.java)!!.value
        val requestBean = RequestBean(type, className, methodName, requestParams)
        val requestJson = gson.toJson(requestBean)

        if (!::webSocketClient.isInitialized) {
            Log.e(TAG, "sendServerFoundRequest: websocket client is not initialized")
        }

        scope.launch {
            Log.i(TAG, "sendServerFoundRequest")
            asyncDeferred.await()
            Log.i(TAG, "sendServerFoundRequest: await end")
            webSocketClient.send(requestJson.toByteArray(Charsets.UTF_8))
        }

        reentrantLock.lock()
        condition.await(2, TimeUnit.SECONDS)
        reentrantLock.unlock()

        return requestResult
    }

    private fun <T> getProxy(clazz: Class<T>): T {
        val classLoader = sContext.classLoader

        return Proxy.newProxyInstance(
            classLoader,
            arrayOf((clazz)),
            MyNameServerInvocationHandler(clazz)
        ) as T
    }

    fun connect(ip: String) {
        asyncDeferred = scope.async {
            Log.i(TAG, "connect: ip: $ip")
            connectSocketServer(ip)
        }
    }

    private fun connectSocketServer(ip: String) {
        val uri = URI("$ip:8302")
        webSocketClient = MyWebSocketClient(uri)
        Log.i(TAG, "connectSocketServer: ${uri.toString()}")
        webSocketClient.connectBlocking()
    }

    class MyWebSocketClient(uri: URI) : WebSocketClient(uri) {
        override fun onOpen(handshakedata: ServerHandshake?) {
            Log.i(TAG, "onOpen: ${handshakedata.toString()}")
        }

        override fun onMessage(message: String?) {
            val requestBean = gson.fromJson(message, RequestBean::class.java)
            Log.i(TAG, "onMessage: $message")
            if (!message.isNullOrEmpty()) {
                requestResult = message
            }
            reentrantLock.lock()
            condition.signal()
            reentrantLock.unlock()
        }


        override fun onMessage(bytes: ByteBuffer?) {
            val byteArray = ByteArray(bytes!!.remaining())
            bytes.get(byteArray)

            val string = String(byteArray)

            val requestBean = gson.fromJson(string, RequestBean::class.java)
        }

        override fun onClose(code: Int, reason: String?, remote: Boolean) {
            Log.i(TAG, "onClose: $reason")
        }

        override fun onError(ex: Exception?) {
            Log.e(TAG, "onError: ${ex.toString()}")
        }

    }
}